﻿using JESAI.Redis.Abstracts;
using JESAI.Redis.Abstracts.Commands;
using JESAI.Redis.Abstracts.RedisClients;
using JESAI.Redis.Abstracts.Resolvers;
using JESAI.Redis.Datas;
using JESAI.Redis.Event;
using JESAI.Redis.Events;
using JESAI.Redis.Exceptions;
using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;

namespace JESAI.Redis.Resolvers
{
    public class DefaultRedisResolver : IRedisResolver
    {
        protected readonly IObjectInstancePool<IRedisClient> _redisClientPool;
        /// <summary>
        /// 
        /// </summary>
        public event EventHandler<CommandExecuteEventArgs> CommandExecute;

        /// <summary>
        /// 
        /// </summary>
        public event EventHandler<CommandExecutedEventArgs> CommandExecuted;
        public DefaultRedisResolver(IObjectInstancePool<IRedisClient> redisClientPool)
        {
            _redisClientPool = redisClientPool;
        }

        public virtual async Task<RedisData> InvokeSimpleAsync(IRedisCommandArg command)
        {
            var eventArgs = new CommandExecuteEventArgs(command);
            CommandExecute?.Invoke(null, eventArgs);
            //判断是否为缓存
            if (eventArgs.IsCache && eventArgs.Value is RedisData result && !result.IsEmpty())
            {
                return result;
            }
            using (var redisClient = await _redisClientPool.GetAsync())
            {
                try
                {
                    var res = await DoWhileAsync(async rc => await rc.ExecuteSampleAsync(command), redisClient);
                    CommandExecuted?.Invoke(null, new CommandExecutedEventArgs(command, res));
                    return res;
                }
                catch (Exception e)
                {
                    CommandExecuted?.Invoke(null, new CommandExecutedEventArgs(command, null, e));
                    redisClient.Close();
                    throw;
                }
            }
        }

        public void RegisterCommandExecutedEvent(EventHandler<CommandExecutedEventArgs> eventHandler)
        {
            CommandExecuted += eventHandler;
        }

        public void RegisterCommandExecuteEvent(EventHandler<CommandExecuteEventArgs> eventHandler)
        {
            CommandExecute += eventHandler;
        }

        public void UnRegisterCommandExecutedEvent(EventHandler<CommandExecutedEventArgs> eventHandler)
        {
            CommandExecuted -= eventHandler;
        }

        public void UnRegisterCommandExecuteEvent(EventHandler<CommandExecuteEventArgs> eventHandler)
        {
            CommandExecute -= eventHandler;
        }

        /// <summary>
        /// 执行
        /// </summary>
        /// <param name="func"></param>
        /// <param name="redisClient"></param>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        protected virtual async Task<T> DoWhileAsync<T>(Func<IRedisClient, Task<T>> func,
            IRedisClient redisClient)
        {
            while (true)
            {
                try
                {
                    //执行命令
                    return await func(redisClient);
                }
                catch (Exception e)
                {
                    EventBus.SelectRedisClientError(redisClient.RedisHost, redisClient.Port, e);
                    //判断异常的类型 是否为网络相关的
                    if (e is not IOException or SocketException)
                    {
                        EventBus.SelectRedisClientError(redisClient.RedisHost, redisClient.Port, e);
                        throw;
                    }

                    //如果是网络问题的话 重置连接，循环判断，直到没有连接可用 抛出异常
                    while (true)
                    {
                        //重置连接
                        try
                        {
                            await redisClient.ReconnectSocketAsync();
                            break;
                        }
                        catch (Exception exception)
                        {
                            EventBus.SelectRedisClientError(redisClient.RedisHost, redisClient.Port, exception);
                            //当没有连接的时候 抛出
                            if (exception is NotFindConnectionException)
                            {
                                throw;
                            }
                            Console.WriteLine($"{DateTime.Now.Ticks}:连接失败");
                        }
                    }
                }
            }
        }
    }
}
